package jtt808client

import (
	"encoding/json"
	"errors"
	"log"
	"net"
	"time"
)

// 客户端配置选项
type Options struct {
	GatewayAddr       string   // 网关地址
	UserName          string   // 用户名
	Password          string   // 密码
	UserType          string   // 用户类型，web:后端，client:桌面客户端，mobile:手机，809:政府监管怕平台，1078：部标视频，third：第三方，other：其他， 默认为web
	TerminalPhoneList []string // 订阅终端手机号列表, 为空则订阅所有终端
	/*
		订阅消息类型列表：
			* common_response：终端通用应答
			* heartbeat：终端心跳
			* cancel：终端注销
			* register：终端注册
			* auth：终端鉴权
			* params：终端参数
			* properties：终端属性
			* location：位置上报
			* get_location：位置信息查询应答
			* get_region: 查询区域或线路数据应答（2019版本支持）
		    * recorder：行驶记录数据上传
			* waybill：电子运单
			* driver：驾驶员
			* batch_location：定位数据批量上传
			* media_event：多媒体事件信息上传
			* media_data：多媒体数据上传
			* get_media：多媒体数据检索应答
			* take_photo_reply：摄像头立即拍摄命令应答
			* take_photo_reply：摄像头立即拍摄命令应答
			* data_uplink：数据上行透传
			* audio_video_properties：终端上传音视频属性
			* passenger_flow：终端上传乘客流量
			* audio_video_resource：终端上传音视频资源列表
			* file_upload_notify：文件上传完成通知
	*/
	Subscriptions []string
	Event         EventHandler // 事件处理函数
}

// 客户端
type jTT808Client struct {
	Opt           Options      // 网关配置
	conn          net.Conn     // 连接实例
	connectStatus bool         // 连接状态
	CanWrite      bool         // 可写状态
	Event         EventHandler // 事件处理接口
}

// 客户端对象
var client *jTT808Client

// 获取客户端实例
func Client() *jTT808Client {
	return client
}

// 网关链路检测
func StartConnectCheck() {
	go func() {
		for {
			// 每隔3秒检查一次连接状态
			time.Sleep(3 * time.Second)
			if client != nil && !client.connectStatus {
				log.Println("网关已断开，尝试重连...")
				err := Open(client.Opt)
				if err != nil {
					log.Println("网关连接失败", err.Error())
				} else {
					log.Println("网关重连成功")
				}
			}
		}
	}()
}

/*
打开并登录到网关
  - opt: 网关配置
*/
func Open(opt Options) (err error) {
	if client != nil && client.connectStatus {
		return
	}
	// 连接网关
	conn, err := net.Dial("tcp", opt.GatewayAddr)
	if err != nil {
		client = &jTT808Client{
			Opt:           opt,
			connectStatus: false,
		}
		return
	}
	client = &jTT808Client{
		Opt:           opt,
		conn:          conn,
		connectStatus: true,
		Event:         opt.Event,
	}
	// 登录到网关
	client.sendObjWithoutCanWrite(&GatewayData{
		ClientType: ClientType,
		MsgType:    Login,
		NetType:    NetType,
		Addr:       conn.LocalAddr().String(),
		Body: loginRequest{
			UserName:          opt.UserName,
			Password:          opt.Password,
			UserType:          opt.UserType,
			TerminalPhoneList: opt.TerminalPhoneList,
			Subscriptions:     opt.Subscriptions,
		},
	})
	// 启动一个goroutine来处理服务器发送的数据
	go handleServer(conn)
	return
}

/*
订阅终端和消息类型
  - terminalPhoneList: 终端手机号列表, 为空则订阅所有终端
  - subscriptions: 订阅消息类型列表
*/
func (c *jTT808Client) SubcribeTerminalAndMsgType(terminalPhoneList, subscriptions []string) {
	c.Opt.TerminalPhoneList = terminalPhoneList
	c.Opt.Subscriptions = subscriptions
	c.sendObj(&GatewayData{
		ClientType: ClientType,
		MsgType:    Subcribe,
		NetType:    NetType,
		Body: subcribeRequest{
			TerminalPhoneList: terminalPhoneList,
			Subscriptions:     subscriptions,
		},
	})
}

/*
订阅808透传
  - body: 参数数据-TransparentReq
*/
func (c *jTT808Client) Subcribe808Transparent(body TransparentReq) {
	c.sendObj(&GatewayData{
		ClientType: ClientType,
		MsgType:    Transparent,
		NetType:    NetType,
		Body:       body,
	})
}

/*
设置终端参数
  - terminalPhone: 终端手机号
  - params: 终端参数字典
*/
func (c *jTT808Client) SetTerminalParams(terminalPhone string, params map[uint32]interface{}) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8103,
			Phone: terminalPhone,
		},
		Body: params,
	}
	err = c.sendObj(data)
	return
}

/*
查询终端所有参数
  - terminalPhone: 终端手机号
*/
func (c *jTT808Client) QueryAllTerminalParams(terminalPhone string) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8104,
			Phone: terminalPhone,
		},
	}
	err = c.sendObj(data)
	return
}

/*
查询指定终端参数
  - terminalPhone: 终端手机号
  - idList: 参数ID列表
*/
func (c *jTT808Client) QueryTerminalParamsByIds(terminalPhone string, idList []uint32) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8106,
			Phone: terminalPhone,
		},
		Body: idList,
	}
	err = c.sendObj(data)
	return
}

/*
终端控制
  - terminalPhone: 终端手机号
  - body: 终端控制参数数据
*/
func (c *jTT808Client) TerminalControl(terminalPhone string, body TerminalControlRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8105,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
查询终端属性
  - terminalPhone: 终端手机号
*/
func (c *jTT808Client) QueryTerminalProperties(terminalPhone string) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8107,
			Phone: terminalPhone,
		},
	}
	err = c.sendObj(data)
	return
}

/*
位置信息查询
  - terminalPhone: 终端手机号
  - body: 参数数据-TrackRequest
*/
func (c *jTT808Client) GetLocation(terminalPhone string) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8201,
			Phone: terminalPhone,
		},
	}
	err = c.sendObj(data)
	return
}

/*
临时位置跟踪
  - terminalPhone: 终端手机号
  - body: 参数数据-TrackRequest
*/
func (c *jTT808Client) Track(terminalPhone string, body TrackRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8202,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
人工确认报警消息
  - terminalPhone: 终端手机号
  - body: 参数数据-AckAlarmRequest
*/
func (c *jTT808Client) AckAlarm(terminalPhone string, body AckAlarmRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8203,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
链路检测（2019版本支持）
  - terminalPhone: 终端手机号
*/
func (c *jTT808Client) LinkCheck(terminalPhone string) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8204,
			Phone: terminalPhone,
		},
	}
	err = c.sendObj(data)
	return
}

/*
文本信息下发
  - terminalPhone: 终端手机号
  - body: 参数数据-TextRequest
*/
func (c *jTT808Client) SendText(terminalPhone string, body TextRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8300,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
电话回拨
  - terminalPhone: 终端手机号
  - body: 参数数据-PhoneCallBackRequest
*/
func (c *jTT808Client) PhoneCallBack(terminalPhone string, body PhoneCallBackRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8400,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
设置电话本
  - terminalPhone: 终端手机号
  - body: 参数数据-SetPhoneBookRequest
*/
func (c *jTT808Client) SetPhoneBook(terminalPhone string, body SetPhoneBookRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8401,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
车辆控制
  - terminalPhone: 终端手机号
  - body: 参数数据-items，控制项，具体参考协议文档
  - 2013版本传值参考：items := make([]map[uint16]interface{}, 0);items = append(items, map[uint16]interface{}{0: 1})，值为控制类型
*/
func (c *jTT808Client) CarControll(terminalPhone string, items []map[uint16]interface{}) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8500,
			Phone: terminalPhone,
		},
		Body: items,
	}
	err = c.sendObj(data)
	return
}

/*
设置圆形区域
  - terminalPhone: 终端手机号
  - body: 参数数据-CircleRegion
*/
func (c *jTT808Client) SetCircleRegion(terminalPhone string, body CircleRegion) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8600,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
设置矩形区域
  - terminalPhone: 终端手机号
  - body: 参数数据-RectangRegion
*/
func (c *jTT808Client) SetRectangleRegion(terminalPhone string, body RectangRegion) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8602,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
设置多边形区域
  - terminalPhone: 终端手机号
  - body: 参数数据-PolygonRegion
*/
func (c *jTT808Client) SetPolygonRegion(terminalPhone string, body PolygonRegion) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8604,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
设置线路区域
  - terminalPhone: 终端手机号
  - body: 参数数据-LineRegion
*/
func (c *jTT808Client) SetLineRegion(terminalPhone string, body LineRegion) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8606,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
删除区域
  - terminalPhone: 终端手机号
  - regionType: 区域类型，1：圆形区域，2：矩形区域，3：多边形区域，4：线路区域
  - ids: 区域ID列表
*/
func (c *jTT808Client) DeleteRegion(terminalPhone string, regionType byte, ids []uint32) (err error) {
	if msgId, ok := RegionType[regionType]; ok {
		data := &GatewayData{
			ClientType: ClientType,
			MsgType:    Command808,
			NetType:    NetType,
			Header: MsgHeader{
				MsgId: msgId,
				Phone: terminalPhone,
			},
			Body: ids,
		}
		err = c.sendObj(data)
	}
	return
}

/*
查询区域或线路数据（2019版支持）
  - terminalPhone: 终端手机号
  - regionType: 区域类型，1：圆形区域，2：矩形区域，3：多边形区域，4：线路区域
  - ids: 区域ID列表，如果为空则查询所有区域或线路数据
*/
func (c *jTT808Client) QueryRegion(terminalPhone string, regionType int, ids []uint32) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8608,
			Phone: terminalPhone,
		},
		Body: map[string]interface{}{
			"regionType": regionType,
			"ids":        ids,
		},
	}
	err = c.sendObj(data)
	return
}

/*
行驶记录数据采集命令
  - terminalPhone: 终端手机号
  - body: 参数数据-QueryRecorderParamsRequest
*/
func (c *jTT808Client) QueryRecorderParams(terminalPhone string, body QueryRecorderParamsRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8700,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
行驶记录参数下传命令
  - terminalPhone: 终端手机号
  - body: 参数数据-SetRecorderParamsRequest
*/
func (c *jTT808Client) SetRecorderParams(terminalPhone string, body SetRecorderParamsRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8701,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
上报驾驶员身份信息请求
  - terminalPhone: 终端手机号
*/
func (c *jTT808Client) RequestDriverInfo(terminalPhone string) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8702,
			Phone: terminalPhone,
		},
	}
	err = c.sendObj(data)
	return
}

/*
多媒体数据上传应答
  - terminalPhone: 终端手机号
  - body: 参数数据-AckMediaDataUploadRequest
*/
func (c *jTT808Client) AckMediaDataUpload(terminalPhone string, body AckMediaDataUploadRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8800,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
摄像头立即拍摄命令
  - terminalPhone: 终端手机号
  - body: 参数数据-TakePhotoRequest
*/
func (c *jTT808Client) TakePhoto(terminalPhone string, body TakePhotoRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8801,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
存储多媒体数据检索
  - terminalPhone: 终端手机号
  - body: 参数数据-QueryMediaDataRequest
*/
func (c *jTT808Client) QueryMediaData(terminalPhone string, body QueryMediaDataRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8802,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
存储多媒体数据上传命令
  - terminalPhone: 终端手机号
  - body: 参数数据-UploadMediaDataRequest
*/
func (c *jTT808Client) UploadMediaData(terminalPhone string, body UploadMediaDataRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8803,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
录音开始命令
  - terminalPhone: 终端手机号
  - body: 参数数据-SoundRecordingRequest
*/
func (c *jTT808Client) SoundRecording(terminalPhone string, body SoundRecordingRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8804,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
单条存储多媒体数据检索上传命令
  - terminalPhone: 终端手机号
  - body: 参数数据-QueryMediaDataSingleRequest
*/
func (c *jTT808Client) QueryMediaDataSingle(terminalPhone string, body QueryMediaDataSingleRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8805,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
数据下行透传
  - terminalPhone: 终端手机号
  - body: 参数数据-DataDownTransRequest
*/
func (c *jTT808Client) DataUpTrans(terminalPhone string, body DataDownTransRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x8900,
			Phone: terminalPhone,
		},
	}
	err = c.sendObj(data)
	return
}

/*
查询音视频属性
  - terminalPhone: 终端手机号
*/
func (c *jTT808Client) QueryAudioVideoProperties(terminalPhone string) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x9003,
			Phone: terminalPhone,
		},
	}
	err = c.sendObj(data)
	return
}

/*
实时音视频传输请求
  - terminalPhone: 终端手机号
  - body: 参数数据-RealTimeVideoRequest
*/
func (c *jTT808Client) RequestRealTimeVideo(terminalPhone string, body RealTimeVideoRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x9101,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
音视频实时传输控制
  - terminalPhone: 终端手机号
  - body: 参数数据-RealTimeVideoControlRequest
*/
func (c *jTT808Client) RealTimeVideoControl(terminalPhone string, body RealTimeVideoControlRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x9102,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
实时音视频传输状态通知
  - terminalPhone: 终端手机号
  - body: 参数数据-RealTimeVideoStatusNotifyRequest
*/
func (c *jTT808Client) RealTimeVideoStatusNotify(terminalPhone string, body RealTimeVideoStatusNotifyRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x9105,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
远程录像回放请求
  - terminalPhone: 终端手机号
  - body: 参数数据-HistoryVideoRequest
*/
func (c *jTT808Client) RequestHistoryVideo(terminalPhone string, body HistoryVideoRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x9201,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
远程录像回放控制
  - terminalPhone: 终端手机号
  - body: 参数数据-HistoryVideoControlRequest
*/
func (c *jTT808Client) HistoryVideoControl(terminalPhone string, body HistoryVideoControlRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x9202,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
查询音视频资源列表
  - terminalPhone: 终端手机号
  - body: 参数数据-AudioVideoResourceRequest
*/
func (c *jTT808Client) QueryAudioVideoResource(terminalPhone string, body AudioVideoResourceRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x9205,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
音视频文件上传请求
  - terminalPhone: 终端手机号
  - body: 参数数据-AudioVideoFileUploadRequest
*/
func (c *jTT808Client) RequestAudioVideoFileUpload(terminalPhone string, body AudioVideoFileUploadRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x9206,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
音视频文件上传控制
  - terminalPhone: 终端手机号
  - body: 参数数据-AudioVideoFileUploadControlRequest
*/
func (c *jTT808Client) AudioVideoFileUploadControl(terminalPhone string, body AudioVideoFileUploadControlRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x9207,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
AI报警附件上传指令
  - terminalPhone: 终端手机号
  - body: 参数数据-AIAlarmFileUploadRequest
*/
func (c *jTT808Client) AIAlarmFileUpload(terminalPhone string, body AIAlarmFileUploadRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x9208,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
AI文件上传完成消息应答
  - terminalPhone: 终端手机号
  - body: 参数数据-AIAlarmFileUploadCompleteRequest
*/
func (c *jTT808Client) AIAlarmFileUploadComplete(terminalPhone string, body AIAlarmFileUploadCompleteRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x9212,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
云台旋转
  - terminalPhone: 终端手机号
  - body: 参数数据-PTZRotateRequest
*/
func (c *jTT808Client) PTZRotate(terminalPhone string, body PTZRotateRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x9301,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
云台调整焦距控制
  - terminalPhone: 终端手机号
  - body: 参数数据-PTZFocalDistanceRequest
*/
func (c *jTT808Client) PTZFocalDistance(terminalPhone string, body PTZFocalDistanceRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x9302,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
云台调整光圈控制
  - terminalPhone: 终端手机号
  - body: 参数数据-PTZApertureRequest
*/
func (c *jTT808Client) PTZAperture(terminalPhone string, body PTZApertureRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x9303,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
云台雨刷控制
  - terminalPhone: 终端手机号
  - body: 参数数据-PTZWindshieldWiperRequest
*/
func (c *jTT808Client) PTZWindshieldWiper(terminalPhone string, body PTZWindshieldWiperRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x9304,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
云台红外不光控制
  - terminalPhone: 终端手机号
  - body: 参数数据-PTZInfraredRequest
*/
func (c *jTT808Client) PTZInfrared(terminalPhone string, body PTZInfraredRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x9305,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

/*
云台变倍控制
  - terminalPhone: 终端手机号
  - body: 参数数据-PTZZoomRequest
*/
func (c *jTT808Client) PTZZoom(terminalPhone string, body PTZZoomRequest) (err error) {
	data := &GatewayData{
		ClientType: ClientType,
		MsgType:    Command808,
		NetType:    NetType,
		Header: MsgHeader{
			MsgId: 0x9306,
			Phone: terminalPhone,
		},
		Body: body,
	}
	err = c.sendObj(data)
	return
}

// 发送数据
func (c *jTT808Client) sendObjWithoutCanWrite(obj interface{}) (err error) {
	data, _ := json.Marshal(obj)
	data = append(GFrameHeaderBytes, data...)
	data = append(data, GFrameEndBytes...)
	if c.connectStatus {
		_, err = c.conn.Write(data)
		if err != nil {
			c.connectStatus = false
		}
	}
	return
}

func (c *jTT808Client) sendObj(obj interface{}) (err error) {
	data, _ := json.Marshal(obj)
	data = append(GFrameHeaderBytes, data...)
	data = append(data, GFrameEndBytes...)
	if c.connectStatus && c.CanWrite {
		_, err = c.conn.Write(data)
		if err != nil {
			c.connectStatus = false
		}
	} else {
		err = errors.New("client not connect or can't write")
	}
	return
}
